/**
 *
 */
package com.wedroid.r2d2.message;

import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.demo2do.core.BusinessException;
import com.wedroid.entity.message.MessageType;
import com.wedroid.r2d2.Constant;
import com.wedroid.r2d2.contact.Contact;
import com.wedroid.r2d2.contact.ContactManager;
import com.wedroid.r2d2.http.RequestManager;
import com.wedroid.r2d2.message.RipeMessage.RipeMessageBuilder;
import com.wedroid.r2d2.message.handler.CompositeMessageHandler;
import com.wedroid.r2d2.message.parser.CompositeContentParser;
import com.wedroid.r2d2.support.ResponseResult;
import com.wedroid.r2d2.support.response.SyncResponse;

/**
 * @author lute
 *
 */
@Component
public class MessageManager {

    private static final Logger logger = LoggerFactory.getLogger(MessageManager.class);

    @Autowired
    private RequestManager requestManager;

    @Autowired
    private ContactManager contactManager;

    @Autowired
    private CompositeMessageHandler messageHandler;

    @Autowired
    private CompositeContentParser contentParser;

    /**
     * Sync messages from WeChat
     */
    public void syncMessages() {
        try {

            ResponseResult<SyncResponse> result = requestManager.sync();
            if (result.isValid()) {
                SyncResponse response = result.getValue();

                // Handle modified contact and group members first
                List<Contact> modifiedContacts = response.getModifiedContacts();
                if (CollectionUtils.isNotEmpty(modifiedContacts)) {
                    logger.trace("#syncMessage - Going to handle contact modification...");
                    contactManager.handleContactModification(modifiedContacts);
                }

                // Handle message
                List<RawMessage> rawMessages = response.getRawMessages();
                for (RawMessage rawMessage : rawMessages) {
                    handleMessage(rawMessage);
                }
            } else {
                throw new BusinessException(result.getMessage());
            }
        } catch (RuntimeException e) {
            logger.error("#syncMessages - Get runtime exception and its cause is {}", e.getCause());
        }
    }

    /**
     * Handle and parse message content
     * @param rawMessage
     */
    private void handleMessage(RawMessage rawMessage) {

        // Extract message from raw message
        RipeMessage message = parseRawMessage(rawMessage);

        // Add message into queue
        messageHandler.handleMessage(message);
    }

    /**
     * @param rawMessage
     * @return
     */
    private RipeMessage parseRawMessage(RawMessage rawMessage) {
        String msgId = rawMessage.getMsgId();
        MessageType messageType = MessageType.codeOf(rawMessage.getMsgType());
        long elapsedSeconds = rawMessage.getCreateTime();

        // Get sender
        String fromUserName = rawMessage.getFromUserName();
        Contact sender = contactManager.getContact(fromUserName);

        // Get receiver
        String toUserName = rawMessage.getToUserName();
        Contact receiver = contactManager.getContact(toUserName);
        if (!receiver.getUserName().startsWith("@") || receiver.getUserName().length() <= 1) {
            receiver = contactManager.getDroid();
        }

        // Construct ripe message builder
        RipeMessageBuilder messageBuilder = new RipeMessageBuilder(msgId, messageType, elapsedSeconds)
                                                    .sender(sender, decideUserName(sender))
                                                    .receiver(receiver, decideUserName(receiver))
                                                    .droid(contactManager.getDroid());

        // Escape message content
        String content = StringEscapeUtils.unescapeHtml4(rawMessage.getContent());

        if (sender.isGroup()) {
            // Filter out the message sender's user name and preferred name of group message.
            // In group message, `FromUserName` is the name of group. And the message has
            // the form of [UserName:<br/>[real message content]]. Therefore, those steps
            // below is for extracting the `UserName` and getting the preferred name of that user.
            int breakIdx = content.indexOf("<br/>");
            if (breakIdx > -1) {
                String uid = StringUtils.substring(content.substring(0, breakIdx), 0, -1);
                content = content.substring(breakIdx).replace("<br/>", "");
                Contact fromUser = contactManager.getGroupMember(rawMessage.getFromUserName(), uid);

                if (uid != null) {
                    if (fromUser != null) {
                        messageBuilder.fromUser(fromUser);
                    } else {
                        logger.error("#parseRawMessage - Extract group member name [{}], but do not get the group member from group", uid);
                    }
                }
            }
        }

        // Parse data
        String data = contentParser.parse(rawMessage, content);
        RipeMessage message = messageBuilder.data(data).build();
        return message;
    }

    /**
     * Decide from user's preferred name of message
     * @param messageSource
     * @param contact
     * @return
     */
    private String decideUserName(Contact contact) {
        String name = contactManager.getContactPreferredName(contact);
        if (StringUtils.isBlank(name)) {
            name = Constant.UNKNOWN;
        }
        return StringEscapeUtils.unescapeHtml4(name);
    }

}
